package fr.lelouet.ga.stoppers;

import fr.lelouet.ga.Generation;
import fr.lelouet.ga.Stopper;
import fr.lelouet.ga.reducers.Evaluator;

/**
 * a {@link Stopper} that stops an algorithm after it didn't reach enough
 * increase in its generations' max values for a given number of iterations
 */
public class FewDifferenceStopper<T> implements Stopper<T> {

  protected final double mindiff;

  /** lastvalues[i] is the max value of an element of the previous i generation */
  protected final double[] lastvalues;

  protected final Evaluator<T> evaluator;

  protected int generationDone = 0;

  public FewDifferenceStopper(double minDifference, int genNumber,
      Evaluator<T> evaluator) {
    this.mindiff = minDifference;
    this.lastvalues = new double[genNumber + 1];
    this.evaluator = evaluator;
  }

  @Override
  public void restart() {
    for (int i = 0; i < lastvalues.length - 1; i++) {
      lastvalues[i] = Double.NEGATIVE_INFINITY;
    }
  }

  @Override
  public boolean stop(Generation<T> generation) {
    double maxval = Double.NEGATIVE_INFINITY;
    // T bestElem = null;
    for (T elem : generation) {
      double val = evaluator.eval(elem);
      if (val > maxval) {
        // bestElem = elem;
        maxval = val;
      }
    }
    // System.out.println("max val for this generation : " + maxval +
    // " for elem "
    // + bestElem);
    for (int i = lastvalues.length - 1; i > 0; i--) {
      lastvalues[i] = lastvalues[i - 1];
    }
    lastvalues[0] = maxval;
    generationDone++;
    if (generationDone <= lastvalues.length + 1) {
      return false;
    }
    for (double d : lastvalues) {
      if (maxval - d >= mindiff) {
        return false;
      }
    }
    return true;
  }
}
